import sys
from collections import namedtuple
import pygame
import random

from game.system.go.component.MyButton import MyButton
from game.system.go.component.Picture import Picture

Position = namedtuple('Position', ['x', 'y'])


# 围棋逻辑处理
class GoRuleManager(object):


    pic = Picture()
    # 棋盘格子位置相关
    top, left, space, lines = (20, 20, 36, 19)
    color = (0, 0, 0)  # 棋盘格子线颜色 黑
    WHITE = (255, 255, 255)
    GREEN = (0, 255, 0)
    RED = (255, 0, 0)

    # 黑子先手
    blackTurn = True
    CHESSBOARD_SIZE = 19
    # 类型：黑子
    BLACK_TYPE = -1
    # 类型：白子
    WHITE_TYPE = 1

    # 记录路径顺序， key:坐标 value:棋子类型，第几步
    ballCoordMap = {}
    # 记录第几步
    step = 0
    # 模拟棋盘 黑-1 白1 空0
    momiBoardData =  [[0 for i in range(19)] for j in range(19)]
    # 路径，走过得1 没走过0
    visit = [[0 for i in range(19)] for j in range(19)]

    #todo 记录历史棋盘数据，用于判断打劫
    momiHistoryBoardData = [[0 for i in range(19)] for j in range(19)]

    # 有无气的标志位
    isAliveFlag = 0

    # 是否禁止落子
    banDrop = False

    deadBlackNumber = 0
    deadWhiteNumber = 0

    currDownType = BLACK_TYPE if blackTurn else WHITE_TYPE

    def init(self):
        try:
            # 加载背景图
            self.chessboard = pygame.image.load(self.pic.backgroundFilename)
            # 加载黑白子图片
            self.whiteChessBall = pygame.image.load(self.pic.whiteChessBallFileName[0]).convert_alpha()
            self.blackChessBall = pygame.image.load(self.pic.blackChessBallFileName[0]).convert_alpha()
            self.blackDeadImg = pygame.image.load(self.pic.blackChessBallFileName[1]).convert_alpha()
            self.whiteDeadImg = pygame.image.load(self.pic.whiteChessBallFileName[1]).convert_alpha()
            # 配置字体
            self.font = pygame.font.SysFont('arial', 15)
            self.ChinaFont = pygame.font.Font('./font/simhei.ttf', 20)
            # 棋子图片的矩形，可以获取图片长宽
            self.ballRect = self.whiteChessBall.get_rect()
            # 棋盘上点的位置
            self.getPointLocation()
            # 画棋盘格子线
            self.drawBoard()
            #  画黑白死子数显示屏
            self.drawAliveOrDeadNumber()
            #  画按钮显示手数
            self.drawStepNumber()
            # 画万宁大法按钮
            self.drawButtonKuso()
            # 重新开始按钮
            self.drawButtonStartAgain()

        except pygame.error as e:
            print(e)
            sys.exit()

    # 获取逻辑位置集合
    def getPointLocation(self):
        self.points = [[] for i in range(self.lines)]
        for i in range(self.lines):
            for j in range(self.lines):
                self.points[i].append(Position(self.left + i * self.space,self.top + j * self.space))
        #print(self.points)

    # 获取当前背景图
    def getChessboard(self):
        return self.chessboard

    # 落子，在(i,j)位置落子
    def handleRuleDropAt(self, i, j):

        self.banDrop = False

        # 当前落子类型
        currDownType = self.BLACK_TYPE if self.blackTurn else self.WHITE_TYPE


        # 判断吃子逻辑
        self.isEat(i,j,currDownType)
        if self.banDrop == True:
            return

        self.drop_at_one(currDownType, i, j)
        self.repeatDrawBoard()
        self.blackTurn = not self.blackTurn  # 切换黑白子顺序


    # 落子，在(i,j)位置落子
    def drop_at_one(self, currDownType, i, j):

        self.ballCoordMap.setdefault(Position(i, j), [currDownType, self.step])

        self.step = self.step + 1
        print("-----第"+str(self.step)+"手-----")
        # 获取落子图片位置
        pos_x = self.points[i][j].x - int(self.ballRect.width / 2)
        pos_y = self.points[i][j].y - int(self.ballRect.height / 2)
        if self.blackTurn:  # 轮到黑子下
            self.chessboard.blit(self.blackChessBall, (pos_x, pos_y))
        else:
            self.chessboard.blit(self.whiteChessBall, (pos_x, pos_y))

    # 画棋盘上的格子线，如果棋盘背景图做的足够精确，可省略此步骤
    def drawBoard(self):
        # 画坐标数字
        for i in range(0, self.lines):
            coord_text = self.font.render(str(i+1), True, self.color)
            self.chessboard.blit(coord_text, (
                self.points[i][0].x - round(coord_text.get_width() / 2), self.points[i][0].y - coord_text.get_height()))
            self.chessboard.blit(coord_text, (
                self.points[0][i].x - coord_text.get_width(), self.points[0][i].y - round(coord_text.get_height() / 2)))

        for i in range(self.lines):
            # 画横线
            pygame.draw.line(self.chessboard, self.color,self.points[0][i], self.points[self.lines - 1][i])
            # 画竖线
            pygame.draw.line(self.chessboard, self.color, self.points[i][0],  self.points[i][self.lines - 1])

        # 边界画横线 685
        pygame.draw.line(self.chessboard, self.color, [1,1], [1,685])
        pygame.draw.line(self.chessboard, self.color, [1,1], [685,1])
        pygame.draw.line(self.chessboard, self.color, [685,1], [685,685])
        # 画竖线
        pygame.draw.line(self.chessboard, self.color, [1,685], [685,685])
        # 在星位和天元画小圆点
        pygame.draw.circle(self.chessboard,self.color , (self.points[3][3].x , self.points[3][3].y),4)
        pygame.draw.circle(self.chessboard,self.color , (self.points[15][3].x , self.points[15][3].y),4)
        pygame.draw.circle(self.chessboard,self.color , (self.points[3][15].x , self.points[3][15].y),4)
        pygame.draw.circle(self.chessboard,self.color , (self.points[15][15].x , self.points[15][15].y),4)
        pygame.draw.circle(self.chessboard,self.color , (self.points[9][9].x , self.points[9][9].y),4)



    # 画黑白死子数显示屏
    def drawAliveOrDeadNumber(self):
        blackText = self.ChinaFont.render(str(self.deadBlackNumber), True, self.color)

        whiteText = self.ChinaFont.render(str(self.deadWhiteNumber), True, self.color)
        #pygame.draw.rect(self.chessboard,self.RED,((700, 50), (90, 30)),0)
        self.chessboard.blit(self.blackDeadImg, (700+3, 50+3))
        self.chessboard.blit(blackText, (752+3, 50+7))
        #pygame.draw.rect(self.chessboard,self.RED,((700, 100), (90, 30)),0)
        self.chessboard.blit(self.whiteDeadImg, (700+3, 100+3))
        self.chessboard.blit(whiteText, (752+3, 100+7))



    # 画按钮显示手数
    def drawStepNumber(self):
        currStep = self.ChinaFont.render("当前手数:"+ str(self.step), True, self.color)
        # pygame.draw.rect(self.chessboard,self.RED,((700, 150), (120, 30)),0)
        self.chessboard.blit(currStep, (700+3, 150+3))
        pass

    # 新增万宁大法按钮
    def drawButtonKuso(self):
        self.kuso1_button = MyButton(self.chessboard,700,200,120,50,  (0, 255, 0), "万宁大法")
        self.kuso1_button.draw_button()

    # 重来按钮
    def drawButtonStartAgain(self):
        self.start_again = MyButton(self.chessboard,700,270,120,50,self.RED, "重新开始")
        self.start_again.draw_button()


    #万宁大法1 杀一损二 逻辑
    def kuso1(self):
        # 双方随机落子
        for i in range(3):
            x = random.randrange(self.CHESSBOARD_SIZE)
            y = random.randrange(self.CHESSBOARD_SIZE)
            if self.momiBoardData[x][y] == 0:
                self.handleRuleDropAt(x,y)

    # 重新游戏
    def startAgain(self):
        self.ballCoordMap = {}
        # 记录第几步
        self.step = 0
        # 模拟棋盘 黑-1 白1 空0
        self.momiBoardData =  [[0 for i in range(19)] for j in range(19)]
        # 路径，走过得1 没走过0
        self.visit = [[0 for i in range(19)] for j in range(19)]
        self.momiHistoryBoardData = [[0 for i in range(19)] for j in range(19)]
        # 有无气的标志位
        self.isAliveFlag = 0
        # 是否禁止落子
        self.banDrop = False
        self.deadBlackNumber = 0
        self.deadWhiteNumber = 0
        self.init()


    # 判断是否已产生胜方
    def check_over(self):
        return False



    # 吃子逻辑判断
    def isEat(self,i,j,currDownType):
        # 模拟落子
        self.momiBoardData[i][j] = currDownType
        # 判断死活
        listPoint = self.removeAtPoint(i,j,currDownType)
        if self.banDrop == True:
            self.momiBoardData[i][j] = 0
            return

        #吃子
        self.eatAt(listPoint)



    # 移除i,j位置的图片 重画画布
    def eatAt(self,listPoint):

        for point in listPoint:
            # 增加死子数
            value = self.ballCoordMap.get(Position(point[0], point[1]))
            print("value:"+str(value))
            if value[0] == self.BLACK_TYPE:
                self.deadBlackNumber = self.deadBlackNumber + 1
            elif value[0] == self.WHITE_TYPE:
                self.deadWhiteNumber = self.deadWhiteNumber + 1
            # 删除该位置的棋子
            print("删除前："+str(self.ballCoordMap))
            self.ballCoordMap.pop(Position(point[0], point[1]))
            print("删除后："+str(self.ballCoordMap))




    # 重画当前页面 组件
    def repeatDrawBoard(self):
        # 重画画布
        self.init()
        # 遍历画其他棋子
        for key in self.ballCoordMap.keys():
            i1 = key.x
            j1 = key.y
            value = self.ballCoordMap.get(key)
            pos_x = self.points[i1][j1].x - int(self.ballRect.width / 2)
            pos_y = self.points[i1][j1].y - int(self.ballRect.height / 2)
            if value[0] == self.BLACK_TYPE:
                self.chessboard.blit(self.blackChessBall, (pos_x, pos_y))
            else:
                self.chessboard.blit(self.whiteChessBall, (pos_x, pos_y))

    # 检查(i,j)位置是否已占用
    def check_at(self, i, j):
        for item in self.ballCoordMap.keys():
            if (i, j) == (item.x,item.y):
                return False
        return True

    # 通过事件物理坐标获取棋子逻辑坐标
    def get_coord(self, pos):
        x, y = pos

        i, j = (0, 0)
        oppo_x = x - self.left
        if oppo_x > 0:
            i = round(oppo_x / self.space)  # 四舍五入取整
        oppo_y = y - self.top
        if oppo_y > 0:
            j = round(oppo_y / self.space)
        return (i, j)




    # 模拟棋盘 移除棋子 ,返回移除得棋子坐标
    def removeAtPoint(self,i,j,currDownType):
        token_list = []
        token_list = self.getDeadChess(self.momiBoardData,currDownType, token_list)

        tempBoardData = self.momiBoardData
        print("对方死子："+str(token_list))
        # 先提掉对方死子
        for x, y in token_list:
            tempBoardData[x][y] = 0
        own_dead_list = []
        #再判断自己方有无死子，如果有则不可落子，将棋盘恢复之前状态
        own_dead_list = self.getDeadChess(tempBoardData, - currDownType, own_dead_list)
        print("我方死子："+str(token_list))
        if len(own_dead_list) > 0:
            # 恢复落子
            print("不可以落子")
            self.banDrop = True
            return []
        else:
            #真正提子
            self.momiBoardData = tempBoardData

        return token_list

    def getDeadChess(self,momiBoardData, currDownType, token_list):
        for i in range(self.CHESSBOARD_SIZE):
            for j in range(self.CHESSBOARD_SIZE):
                # 若当前位置是空,则直接跳过
                if  momiBoardData[i][j] == 0:
                    continue
                # 先不考虑当前落子方
                elif momiBoardData[i][j] == currDownType:
                    continue
                # 判断该棋子有无“气”
                elif self.isAlive(momiBoardData,i, j) == 0:
                    # 记录
                    token_list.append([i, j])
        return token_list

    def DFS(self,momiBoardData, i, j):
        self.visit[i][j] = 1 # 表示此点已经搜索过
        directions = [[i - 1, j], [i + 1, j], [i, j - 1], [i, j + 1]] # 定义上下左右四个方向
        for dx, dy in directions:
            if dx < 0 or dx > self.CHESSBOARD_SIZE-1 or dy < 0 or dy > self.CHESSBOARD_SIZE-1:
                continue # 如果是边框，跳过此方向
            elif self.visit[dx][dy] == 0: # 判断是否此点搜索过
                if momiBoardData[dx][dy] == 0:
                    self.isAliveFlag = 1
                    return # 此点为空位置，即原棋子有气，停止搜索
                elif momiBoardData[dx][dy] == - momiBoardData[i][j]:
                    continue # 对方棋子，跳过此方向
                elif  momiBoardData[dx][dy] ==  momiBoardData[i][j]:
                    self.DFS(momiBoardData,dx, dy) # 己方棋子，递归执行
        # 以上条件都不满足,即所有路径都为死路,该棋子无“气”,停止搜索
        return

    def clear_visit(self):
        self.visit = [[0 for i in range(self.CHESSBOARD_SIZE)] for j in range(self.CHESSBOARD_SIZE)]
        self.isAliveFlag = 0

    # 有无“气”的判断，有气返回1，无气返回0
    def isAlive(self,momiBoardData,x, y):
        isalive = 1 # 用于返回
        # 清空搜索记录
        self.clear_visit()
        # 执行深度优先搜索
        # print(str(momiBoardData))
        self.DFS(momiBoardData,x, y)
        # 有“气”标志为0,则返回0,否则为1
        if self.isAliveFlag == 0:
            isalive = 0
        # 再次清空搜索记录
        self.clear_visit()
        return isalive











